' ****************************************************************************
' Copyright (C) 2009 Tony George
' 
' This program is free software; you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation; either version 2 of the License, or
' (at your option) any later version.
' 
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
' GNU General Public License for more details.
' 
' You should have received a copy of the GNU General Public License
' along with this program; if not, write to the Free Software
' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'
' ****************************************************************************

Imports System.IO
Imports System.Xml
Imports System.Xml.Schema

Module modCommon
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Integer)
    Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Integer, ByRef lpExitCode As Integer) As Integer
    Private Declare Function timeGetTime Lib "winmm.dll" () As Integer
    Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Integer, ByVal bInheritHandle As Integer, ByVal dwProcessId As Integer) As Integer
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer
    Private Const STILL_ACTIVE As Short = &H103S
    Private Const PROCESS_QUERY_INFORMATION As Short = &H400S

    Public Const KB As Long = 1024
    Public Const MB As Long = 1024 * 1024
    Public Const GB As Long = 1024 * 1024 * 1024

    Public Const MIN As Integer = 60
    Public Const HR As Integer = 60 * 60

    'Sub setWindowSize(ByVal frm As Form, ByVal pForm As Panel)
    '    Dim WindowBorder, WindowBorderTop As Integer
    '    WindowBorder = CInt((frm.Width - frm.ClientRectangle.Width) / 2)
    '    WindowBorderTop = frm.Height - frm.ClientRectangle.Height - WindowBorder
    '    frm.Height = pForm.Location.Y + pForm.Height + WindowBorderTop + WindowBorder
    '    frm.Width = pForm.Location.X + pForm.Width + WindowBorder + WindowBorder
    'End Sub

    'Sub getWindowBorders(ByRef WindowBorder As Integer, ByRef WindowBorderTop As Integer, ByVal frm As Form)
    '    WindowBorder = CInt((frm.Width - frm.ClientRectangle.Width) / 2)
    '    WindowBorderTop = frm.Height - frm.ClientRectangle.Height - WindowBorder
    'End Sub
    Public Class PresetMenuItem
        Public Path As String
        Public ClickHandler As EventHandler

        Sub New(ByVal item_path As String, ByRef click_handler As EventHandler)
            Path = item_path
            ClickHandler = click_handler
        End Sub
    End Class

    Public Sub PresetMenuItem_DropDownOpening(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim parent As ToolStripMenuItem = DirectCast(sender, ToolStripMenuItem)
        Dim child As ToolStripMenuItem
        Dim p As PresetMenuItem = parent.Tag

        parent.DropDownItems.Clear()

        For Each subfolder As String In My.Computer.FileSystem.GetDirectories(p.Path)
            If My.Computer.FileSystem.GetFiles(subfolder, FileIO.SearchOption.SearchAllSubDirectories).Count > 0 Then
                child = New ToolStripMenuItem
                child.Text = Path.GetFileNameWithoutExtension(subfolder)
                child.Tag = New PresetMenuItem(subfolder, p.ClickHandler)
                parent.DropDownItems.Add(child)
                AddHandler child.DropDownOpening, AddressOf PresetMenuItem_DropDownOpening

                'cosmetic - add a dummy child item so that arrow is displayed for parent item
                child.DropDownItems.Add(New ToolStripMenuItem)
            End If
        Next
        For Each File As String In My.Computer.FileSystem.GetFiles(p.Path)
            If Path.GetExtension(File) = ".xml" Then
                child = New ToolStripMenuItem
                child.Text = Path.GetFileNameWithoutExtension(File)
                child.Tag = New PresetMenuItem(File, p.ClickHandler)
                parent.DropDownItems.Add(child)
                AddHandler child.Click, p.ClickHandler
            End If
        Next File
    End Sub


    Sub SetFonts(ByVal ParentContainer As Control, ByVal newFont As Font)
        ParentContainer.Font = newFont
        For Each c As Control In ParentContainer.Controls
            c.Font = newFont
            If c.HasChildren Then
                SetFonts(c, newFont)
            End If
        Next c
    End Sub

    Function getStringFromMillis(ByVal Milliseconds As Long) As String
        Return (TimeSpan.FromSeconds(Math.Floor(Milliseconds / 1000)).ToString() + "." + (Milliseconds Mod 1000).ToString("000"))
    End Function

    Function getMillisFromString(ByVal TimeString As String) As Long
        Dim millis As Long = 0

        If TimeString.Contains(":") Then
            Dim time As New TimeSpan
            If TimeSpan.TryParse(TimeString, time) Then
                millis = Math.Floor(time.TotalMilliseconds)
            End If
        Else
            Dim time As Double
            If Double.TryParse(TimeString, time) Then
                Dim k As Long = Math.Floor(time * 1000)
                millis = Math.Floor(time * 1000)
            End If
        End If

        Return millis
    End Function

    Function getMillisFromString(ByVal StartTime As String, ByVal EndTime As String) As Long
        Return (getMillisFromString(EndTime) - getMillisFromString(StartTime))
    End Function

    Function deleteFile(ByVal file As String) As Boolean
        Try
            If My.Computer.FileSystem.FileExists(file) Then
                My.Computer.FileSystem.DeleteFile(file)
            End If
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function

    Function createDirectory(ByVal directory As String) As Boolean
        Try
            If Not My.Computer.FileSystem.DirectoryExists(directory) Then
                My.Computer.FileSystem.CreateDirectory(directory)
            End If
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function

    Function fileExists(ByVal file As String) As Boolean
        Return (My.Computer.FileSystem.FileExists(file))
    End Function

    Sub AddNumScrollHandlers(ByVal parent As Control)
        For Each c As Control In parent.Controls
            If TypeOf c Is NumericUpDown Then
                AddHandler c.MouseWheel, AddressOf numScrollHandler
            ElseIf c.HasChildren Then
                AddNumScrollHandlers(c)
            End If
        Next c
    End Sub

    Private Sub numScrollHandler(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
        Dim disable As HandledMouseEventArgs = e
        disable.Handled = True

        Dim num As NumericUpDown = DirectCast(sender, NumericUpDown)

        If e.Delta < 0 Then
            If (num.Value - num.Increment) >= num.Minimum Then
                num.Value -= num.Increment
            End If
        Else
            If (num.Value + num.Increment) <= num.Maximum Then
                num.Value += num.Increment
            End If
        End If
    End Sub

    Function saveTextToFile(ByVal file As String, ByVal text As String, ByVal append As Boolean) As Boolean
        Try
            My.Computer.FileSystem.WriteAllText(file, text, append, System.Text.Encoding.ASCII)
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function

    Function loadTextFromFile(ByVal file As String) As String
        If My.Computer.FileSystem.FileExists(file) Then
            Return (My.Computer.FileSystem.ReadAllText(file))
        Else
            Return ""
        End If
    End Function

    Sub unCheckCheckboxes(ByVal p As Control)
        Dim ctr As Control
        For Each ctr In p.Controls
            If TypeOf ctr Is CheckBox Then
                Dim chk As CheckBox = ctr
                chk.Checked = False
            End If
            If ctr.HasChildren Then
                unCheckCheckboxes(ctr)
            End If
        Next ctr
    End Sub

    Function AddQuotes(ByVal s As String) As String
        Return ("""" + s + """")
    End Function

    Sub ListViewColourize(ByVal ListViewInstance As ListView)
        Dim odd As Boolean = True
        For K As Integer = 0 To ListViewInstance.Items.Count - 1
            If odd Then
                ListViewInstance.Items(K).BackColor = Color.White
            Else
                ListViewInstance.Items(K).BackColor = Color.FromArgb(255, 255, 240, 255)
            End If
            odd = Not odd
        Next
    End Sub

    Sub RemoveTextSelection(ByVal Container As Control)
        Dim txt As TextBox
        Dim cmb As ComboBox
        Dim ctl As Control

        For Each ctl In Container.Controls
            If TypeOf ctl Is TextBox Then
                txt = ctl
                txt.Select(0, 0)
            End If
            If TypeOf ctl Is ComboBox Then
                cmb = ctl
                cmb.Select(0, 0)
            End If

            ' Recursively call this function for any container controls.
            If Container.HasChildren Then
                RemoveTextSelection(ctl)
            End If
        Next
    End Sub

    Public Class GetOutOfWindows
        ' ----- Windows constants used in shutdown permissions.
        Const SE_PRIVILEGE_ENABLED As Integer = &H2
        Const TOKEN_QUERY As Integer = &H8
        Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20
        Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege"

        ' ----- Shutdown method flags.
        Private Enum ShutdownMethods As Integer
            Logoff = 0
            Shutdown = 1
            Reboot = 6
        End Enum

        <Runtime.InteropServices.StructLayout( _
        Runtime.InteropServices.LayoutKind.Sequential, Pack:=1)> _
        Private Structure TokenPrivileges
            Public PrivilegeCount As Integer
            Public Luid As Long
            Public Attributes As Integer
        End Structure

        ' ----- External features needed to exit Windows.
        Private Declare Ansi Function AdjustTokenPrivileges _
           Lib "advapi32.dll" _
           (ByVal tokenHandle As IntPtr, _
           ByVal disableAllPrivileges As Boolean, _
           ByRef newState As TokenPrivileges, _
           ByVal bufferLength As Integer, _
           ByVal previousState As IntPtr, _
           ByVal returnLength As IntPtr) As Boolean

        Private Declare Ansi Function ExitWindowsEx Lib "user32.dll" (ByVal flags As Integer, ByVal reason As Integer) As Boolean
        Private Declare Ansi Function GetCurrentProcess Lib "kernel32.dll" () As IntPtr
        Private Declare Ansi Sub LockWorkStation Lib "user32.dll" ()
        Private Declare Ansi Function LookupPrivilegeValueA Lib "advapi32.dll" (ByVal systemName As String, ByVal privilegeName As String, ByRef lookupID As Long) As Boolean
        Private Declare Ansi Function OpenProcessToken Lib "advapi32.dll" (ByVal processHandle As IntPtr, ByVal desiredAccess As Integer, ByRef tokenHandle As IntPtr) As Boolean

        Private Shared Sub PerformExit(ByVal usingMethod As Integer)
            ' ----- Log off, reboot, or shut down the system.
            Dim shutdownPrivileges As TokenPrivileges
            Dim processHandle As IntPtr
            Dim tokenHandle As IntPtr = IntPtr.Zero

            ' ----- Give ourselves the privilege of shutting
            '       down the system. First, obtain the token.
            processHandle = GetCurrentProcess()
            OpenProcessToken(processHandle, _
               TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, tokenHandle)

            ' ----- Adjust the token to enable shutdown permissions.
            shutdownPrivileges.PrivilegeCount = 1
            shutdownPrivileges.Luid = 0
            shutdownPrivileges.Attributes = SE_PRIVILEGE_ENABLED
            LookupPrivilegeValueA(Nothing, SE_SHUTDOWN_NAME, shutdownPrivileges.Luid)
            AdjustTokenPrivileges(tokenHandle, False, _
               shutdownPrivileges, 0, IntPtr.Zero, IntPtr.Zero)

            ' ----- Now shut down the system.
            ExitWindowsEx(usingMethod, 0)
        End Sub

        Public Shared Sub ExitViaLockWorkstation()
            ' ----- Lock the workstation.
            LockWorkStation()
        End Sub

        Public Shared Sub ExitViaLogoff()
            ' ----- Log off the current user.
            PerformExit(ShutdownMethods.Logoff)
        End Sub

        Public Shared Sub ExitViaReboot()
            ' ----- Reboot the system.
            PerformExit(ShutdownMethods.Reboot)
        End Sub

        Public Shared Sub ExitViaShutdown()
            ' ----- Shut down the system.
            PerformExit(ShutdownMethods.Shutdown)
        End Sub
    End Class

    Function ExecuteAndCaptureConsoleOutput(ByVal FileName As String, ByVal Arguments As String) As String
        Dim p As New Process
        With p.StartInfo
            .UseShellExecute = False
            .CreateNoWindow = True
            .FileName = FileName
            .Arguments = Arguments
            .RedirectStandardOutput = True
        End With
        p.Start()

        Do
            System.Windows.Forms.Application.DoEvents()
        Loop Until (p.HasExited)
        p.WaitForExit()

        Return p.StandardOutput.ReadToEnd
    End Function

    Function ExecuteAndCaptureConsoleError(ByVal FileName As String, ByVal Arguments As String) As String
        Dim p As New Process
        With p.StartInfo
            .UseShellExecute = False
            .CreateNoWindow = True
            .FileName = FileName
            .Arguments = Arguments
            .RedirectStandardError = True
        End With
        p.Start()

        Do
            System.Windows.Forms.Application.DoEvents()
        Loop Until (p.HasExited)
        p.WaitForExit()

        Return p.StandardError.ReadToEnd
    End Function

    Sub RunTool(ByVal EXE As String, ByVal Options As String)

        Dim Proc As New Process
        With Proc.StartInfo
            .FileName = EXE
            .Arguments = Options
        End With

        Proc.Start()

        'wait for process to exit
        Do
            System.Windows.Forms.Application.DoEvents()
        Loop Until (Proc.HasExited)
    End Sub

    Sub WasteTime(ByVal X As Double)
        Dim Start, Finish As Double
        Start = Microsoft.VisualBasic.DateAndTime.Timer
        Finish = Start + X
        Do While Microsoft.VisualBasic.DateAndTime.Timer < Finish
            System.Windows.Forms.Application.DoEvents() : Sleep(100)
        Loop
    End Sub

#Region "    String formatting"
    Function FormatString(ByVal FileName As String, ByVal ReplaceUnderscores As Boolean, ByVal Capitalise As Boolean) As String
        If ReplaceUnderscores Then
            FileName = Trim(Replace(FileName, "_", " "))
            If Capitalise Then
                FileName = StrConv(FileName, VbStrConv.ProperCase)
            End If
        End If
        Return FileName
    End Function

    Function FormatFileSize(ByVal size As Long) As String
        If size > 0 Then
            Dim s As String = ""
            Select Case size
                Case 0 To 1024
                    s = size.ToString + " bytes"

                Case KB To 100 * KB
                    s = (size / 1024).ToString("#,###.#") + " KB"

                Case 100 * KB To MB
                    s = (size / 1024).ToString("#,###") + " KB"

                Case MB To 100 * MB
                    s = (size / (1024 * 1024)).ToString("#,###.##") + " MB"

                Case 100 * MB To GB
                    s = (size / (1024 * 1024)).ToString("#,###") + " MB"

                Case GB To 100 * GB
                    s = (size / (1024 * 1024)).ToString("#,###.##") + " GB"

                Case Else
                    s = (size / (1024 * 1024 * 1024)).ToString("#,###.##") + " GB"
            End Select
            Return s
        Else
            Return ""
        End If
    End Function

    Function ToSentenceCase(ByVal s As String)
        Dim a As String = ""
        If s <> "" Then
            Dim b = Split(s)
            a = StrConv(b(0), VbStrConv.ProperCase)
            For k = 1 To b.Length - 1
                a += " " + b(k)
            Next k
        End If
        Return a
    End Function
#End Region

    'Auto-Crops an image
    Sub GetCropValues(ByVal p As Image, ByRef CropLeft As Integer, ByRef CropRight As Integer, ByRef CropTop As Integer, ByRef CropBottom As Integer)
        Dim x, y As Int32 'for use of X,Y Coordinates of pixels
        Dim b As New Bitmap(p) 'image needed to crop
        Dim c As New Color() 'pixel color for use of identifying if background

        Dim intLeft As Int32 = b.Width 'furthest left X coordinate
        Dim intRight As Int32 = 0 'furthest right X coordinate
        Dim intBottom As Int32 = 0 'furthest to the bottom Y coordinate
        Dim intTop As Int32 = b.Height 'furthest to top Y coordinate
        y = 0
        Do Until y = b.Height 'loop through pixels on Y axis until end of image height
            x = 0
            Do Until x = b.Width 'loop through pixels on X axis until end of image width
                c = b.GetPixel(x, y) 'Get the color of the pixel

                If (c.R > 5 And c.G > 5 And c.B > 5) Then 'pixel is not black
                    'Determine if pixel is further left than the value we already have
                    If x < intLeft Then
                        intLeft = x
                    End If
                    'Determine if pixel is further right than the value we already have
                    If x > intRight Then
                        intRight = x
                    End If
                    'Determine if pixel is further to the top than the value we already have
                    If y < intTop Then
                        intTop = y
                    End If
                    'Determine if pixel is further to the bottom than the value we already have
                    If y > intBottom Then
                        intBottom = y
                    End If
                End If
                x += 1
            Loop
            y += 1
        Loop

        CropLeft = intLeft
        If CropLeft Mod 2 = 1 Then
            CropLeft += 1
        End If

        CropRight = b.Width - intRight - 1
        If CropRight Mod 2 = 1 Then
            CropRight += 1
        End If

        CropTop = intTop
        If CropTop Mod 2 = 1 Then
            CropTop += 1
        End If

        CropBottom = b.Height - intBottom - 1
        If CropBottom Mod 2 = 1 Then
            CropBottom += 1
        End If

        'MsgBox(CropLeft & " " & CropRight & " " & CropTop & " " & CropBottom)

        b.Dispose()
    End Sub

#Region "    Color code conversion"
    Function colorToHex(ByVal c As Color) As String
        Dim s As String = ""
        s += byteToHex(c.R)
        s += byteToHex(c.G)
        s += byteToHex(c.B)
        Return s
    End Function

    Function byteToHex(ByVal i As Integer) As String
        Dim s As String = ""
        Dim t As Integer = i

        While (t > 15)
            s = nibbleToHex(t Mod 16) & s
            t = Math.Floor(t / 16)
        End While
        s = nibbleToHex(t) & s

        If s.Length = 1 Then
            s = "0" + s
        End If
        Return s
    End Function

    Function nibbleToHex(ByVal i As Integer) As String
        Dim s As String = ""
        Select Case i
            Case 0 To 9
                s += i.ToString
            Case 10
                s += "A"
            Case 11
                s += "B"
            Case 12
                s += "C"
            Case 13
                s += "D"
            Case 14
                s += "E"
            Case 15
                s += "F"
        End Select
        Return s
    End Function
#End Region

    'Changing image format
    'We can accomplish this using the Save method of Bitmap object
    'Dim btmp As New Bitmap("C:\myimage.bmp")
    'btmp.Save("C:\myimage.jpg",System.Drawing.Imaging.ImageFormat.Jpeg)
#Region "    HTML formatting functions"
    Function htmlEmptyRow() As String
        Return ("<tr>&nbsp;</tr>" + vbNewLine)
    End Function

    Function htmlRow(ByVal value As String, ByVal odd As Boolean) As String
        Dim row = ""
        If odd Then
            row += "<tr class='odd'>"
        Else
            row += "<tr>"
        End If
        row += value + "</tr>"

        Return (row + vbNewLine)
    End Function

    Function htmlIsEmptyRow(ByVal row As String) As Boolean
        Return (row.Replace("<tr>", "").Replace("</tr>", "").Replace("<th>", "").Replace("</th>", "").Replace("<td>", "").Replace("</td>", "").Replace("&nbsp;", "").Trim = "")
    End Function

    Function htmlEmptyHead() As String
        Return ("<th>&nbsp;</th>" + vbNewLine)
    End Function

    Function htmlEmptyColumn() As String
        Return ("<td>&nbsp;</td>" + vbNewLine)
    End Function

    Function htmlHead(ByVal value As String) As String
        Return ("<th>" + value + "</th>" + vbNewLine)
    End Function

    Function htmlColumn(ByVal value As String) As String
        Return ("<td>" + value + "</td>" + vbNewLine)
    End Function
#End Region

#Region "    XML Helper code"
    ' Make and return a new XML node.
    Public Function MakeXmlNode(ByVal parent As XmlNode, ByVal new_name As String, _
     Optional ByVal new_value As String = Nothing) As XmlNode
        Dim doc As XmlDocument
        Dim new_node As XmlNode

        ' Get the parent document.
        doc = parent.OwnerDocument
        If doc Is Nothing Then doc = parent

        new_node = doc.CreateElement(new_name)
        parent.AppendChild(new_node)
        If Not (new_value Is Nothing) Then new_node.InnerXml = new_value

        Return new_node
    End Function

    Public Function MakeXmlNodeAttribute(ByVal parent As XmlNode, ByVal new_name As String, Optional ByVal new_value As String = Nothing) As XmlAttribute
        Dim doc As XmlDocument
        Dim new_att As XmlNode

        ' Get the parent document.
        doc = parent.OwnerDocument
        If doc Is Nothing Then doc = parent

        new_att = doc.CreateAttribute(new_name)
        new_att.Value = new_value

        parent.Attributes.Append(new_att)

        Return new_att
    End Function

    Public Function AttributeValue(ByVal parent As XmlNode, ByVal attribute_name As String, ByVal default_value As String) As String
        Dim att_value As String
        att_value = parent.Attributes.GetNamedItem(attribute_name).Value
        If att_value = "" Then
            Return default_value
        Else
            Return att_value
        End If
    End Function

    ' Return this node's child value.
    Public Function ChildNodeValue(ByVal parent As XmlNode, ByVal value_name As String, _
     ByVal default_value As String) As String
        Dim child_node As XmlNode

        child_node = parent.Item(value_name)
        If child_node Is Nothing Then
            Return default_value
        Else
            Return child_node.InnerText
        End If
    End Function

    Public Function getChildNode(ByVal parent As XmlNode, ByVal node_name As String) As XmlNode
        Dim child_node As XmlNode = Nothing
        If parent IsNot Nothing Then
            child_node = parent.Item(node_name)
        End If
        Return child_node
    End Function
#End Region
End Module

Module SaveFormState

    Sub saveFormStateToFile(ByVal file As String, ByVal parent As Control, ByVal ProfileName As String, ByVal ProfileType As String)
        Dim Doc As New XmlDocument()

        Dim dec As XmlDeclaration = Doc.CreateXmlDeclaration("1.0", Nothing, Nothing)
        Doc.AppendChild(dec)

        Dim DocRoot As XmlElement = Doc.CreateElement("Profile")
        Doc.AppendChild(DocRoot)

        Dim att As XmlAttribute = Doc.CreateAttribute("Name")
        att.Value = ProfileName
        DocRoot.Attributes.Append(att)

        att = Doc.CreateAttribute("Type")
        att.Value = ProfileType
        DocRoot.Attributes.Append(att)

        getSettings(parent, Doc, DocRoot)
        Doc.Save(file)
    End Sub

    Function loadFormStateFromFile(ByVal file As String, ByVal parent As Control, ByVal ProfileType As String) As Boolean
        Dim Doc As New XmlDocument
        Doc.Load(file)
        Dim root As XmlElement = Doc.ChildNodes(1)
        Dim type As String = root.GetAttribute("Type")
        If (type = ProfileType) Then
            setSettings(parent, Doc, Doc.ChildNodes(1))
            Return True
        Else
            Return False
        End If
    End Function

    Function getSettings(ByVal Container As Control, ByRef Doc As XmlDocument, ByRef DocRoot As XmlElement) As String
        Dim s As String = ""
        Dim append As Boolean = False
        Dim ctrl As XmlElement
        Dim attName As XmlAttribute
        Dim attType As XmlAttribute
        Dim attValue As XmlAttribute
        Dim txt As TextBox
        Dim chk As CheckBox
        Dim cmb As ComboBox
        Dim num As NumericUpDown

        For Each c As Control In Container.Controls
            append = False

            ctrl = Doc.CreateElement("Param")
            attName = Doc.CreateAttribute("Name")
            attType = Doc.CreateAttribute("Type")
            attValue = Doc.CreateAttribute("Value")


            If c.Name.Trim = "" Then
                Continue For
            End If

            attName.Value = c.Name

            If TypeOf c Is TextBox Then
                txt = c
                attName.Value = c.Name.Replace("txt", "")
                attType.Value = "Text"
                attValue.Value = txt.Text
                append = True
            ElseIf TypeOf c Is ComboBox Then
                cmb = c
                attName.Value = c.Name.Replace("cmb", "")
                attType.Value = "Selection"
                attValue.Value = cmb.Text
                append = True
            ElseIf TypeOf c Is CheckBox Then
                chk = c
                attName.Value = c.Name.Replace("chk", "")
                attType.Value = "Boolean"
                attValue.Value = chk.Checked.ToString
                append = True
            ElseIf TypeOf c Is NumericUpDown Then
                num = c
                attName.Value = c.Name.Replace("num", "")
                attType.Value = "Number"
                attValue.Value = num.Value.ToString
                append = True
            End If

            If append = True Then
                ctrl.Attributes.Append(attName)
                ctrl.Attributes.Append(attType)
                ctrl.Attributes.Append(attValue)
                DocRoot.AppendChild(ctrl)
            End If

            If c.HasChildren Then
                s += getSettings(c, Doc, DocRoot)
            End If
        Next c
        Return s
    End Function

    Function setSettings(ByVal Container As Control, ByRef Doc As XmlDocument, ByRef DocRoot As XmlElement) As String
        Dim s As String = ""
        Dim c_array() As Control
        Dim c As Control = Nothing
        Dim txt As TextBox
        Dim chk As CheckBox
        Dim cmb As ComboBox
        Dim num As NumericUpDown

        For Each ctrl As XmlElement In DocRoot.ChildNodes
            If ctrl.GetAttribute("Name") = "" Then
                Continue For
            End If

            Select Case ctrl.GetAttribute("Type")
                Case "Text"
                    c_array = Container.Controls.Find("txt" & ctrl.GetAttribute("Name"), True)
                    If c_array.Length <> 1 Then
                        Continue For
                    End If
                    c = c_array(0)

                    txt = c
                    txt.Text = ctrl.GetAttribute("Value")
                Case "Selection"
                    c_array = Container.Controls.Find("cmb" & ctrl.GetAttribute("Name"), True)
                    If c_array.Length <> 1 Then
                        Continue For
                    End If
                    c = c_array(0)

                    cmb = c
                    cmb.SelectedItem = ctrl.GetAttribute("Value")
                Case "Boolean"
                    c_array = Container.Controls.Find("chk" & ctrl.GetAttribute("Name"), True)
                    If c_array.Length <> 1 Then
                        Continue For
                    End If
                    c = c_array(0)

                    chk = c
                    chk.Checked = Boolean.Parse(ctrl.GetAttribute("Value"))
                Case "Number"
                    c_array = Container.Controls.Find("num" & ctrl.GetAttribute("Name"), True)
                    If c_array.Length <> 1 Then
                        Continue For
                    End If
                    c = c_array(0)

                    num = c
                    num.Value = CDec(ctrl.GetAttribute("Value"))
            End Select

            If ctrl.HasChildNodes Then
                s += setSettings(c, Doc, ctrl)
            End If
        Next ctrl

        Return s
    End Function
End Module
